package com.skyyan.netty.jjf.protocol;

import cn.hutool.core.codec.BCD;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import com.skyyan.netty.jjf.util.BcdUtil;
import com.skyyan.netty.jjf.util.ChineseUtils;
import com.skyyan.netty.jjf.util.OrderUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


/**
 * C.9 开卡报文
 * AFN = 0x97
 *
 * 上行报文格式（设备 → 中心）：
 * 起始字符0x68
 * 长度1个字节
 * 起始字符0x68
 * - 控制域：0x01
 * - 地址域：19字节（设备注册号16 + 管理机号3）
 * - AFN：0x97
 * - 数据域：51字节
 *   数据域[0-7]  订单号：BCD码（8字节）
 *   数据域[8-19] 姓名：UTF-8编码，每3字节一个汉字，不足补0
 *   数据域[20-25]电话：BCD码，6字节，高位补0
 *   数据域[26-43]身份证：ASCII码，X为大写，18字节
 *   数据域[44-45]区域号：BCD码，2字节，高位在前
 *   数据域[46-50]卡号：5字节，高位在前，低位在后（HEX）
 *   CRC
 *
 * 下行报文格式（中心 → 设备）：
 * - 控制域：0x00
 * - 地址域：同上
 * - AFN：0x97
 * - 数据域：25字节
 *   [0-7]  订单号：BCD码（8字节）
 *   [8-23] 农户注册编号：16字节，低位在前，高位在后（HEX）
 *   [24]   状态：BCD码，00=成功，01=已注册，02=失败
 *
 * @author skyyan
 */
public class OpenCardMessage extends Message {

    private static final Logger log = LoggerFactory.getLogger(OpenCardMessage.class);

    private static final Charset UTF8 = StandardCharsets.UTF_8;
    private static final Charset ASCII = StandardCharsets.US_ASCII;

    // 数据长度
    private static final int UPLINK_DATA_LEN = 51;
    private static final int DOWNLINK_DATA_LEN = 25;

    // 上行偏移
    private static final int OFFSET_ORDER_ID_UP = 0;
    private static final int OFFSET_NAME_UP = 8;
    private static final int OFFSET_PHONE_UP = 20;
    private static final int OFFSET_ID_CARD_UP = 26;
    private static final int OFFSET_AREA_CODE_UP = 44;
    private static final int OFFSET_CARD_NO_UP = 46;

    // 下行偏移
    private static final int OFFSET_ORDER_ID_DOWN = 0;
    private static final int OFFSET_FARMER_ID_DOWN = 8;
    private static final int OFFSET_STATUS_DOWN = 24;

    public OpenCardMessage(byte control, byte[] address, byte afn, byte[] userData) {
        super(control, address, afn, userData);
    }

    /**
     * 将字节数组填充到指定长度
     * @param source 源数组
     * @param targetLength 目标长度
     * @param fillByte 填充字节
     * @return 填充后的数组
     */
    private static byte[] padToLength(byte[] source, int targetLength, byte fillByte) {
        if (source == null) {
            source = new byte[0];
        }
        if (source.length >= targetLength) {
            return Arrays.copyOf(source, targetLength);
        }
        byte[] result = new byte[targetLength];
        System.arraycopy(source, 0, result, 0, source.length);
        Arrays.fill(result, source.length, targetLength, fillByte);
        return result;
    }

    /**
     * 去除字节数组末尾指定的字节
     * @param source 源数组
     * @param trimByte 需要去除的字节
     * @return 去除后的数组
     */
    private static byte[] trimEnd(byte[] source, byte trimByte) {
        if (source == null || source.length == 0) {
            return source;
        }
        int endIndex = source.length;
        while (endIndex > 0 && source[endIndex - 1] == trimByte) {
            endIndex--;
        }
        return Arrays.copyOf(source, endIndex);
    }

    // ========================================================================
    // ========================= 上行报文：设备 → 中心 ==========================
    // ========================================================================

    public static class Uplink extends OpenCardMessage {
        //订单号
        private String orderId;
        //姓名
        private String name;
        //电话
        private String phone;
        //身份证号
        private String idCard;
        //区域号
        private int areaCode;
        //卡号
        private byte[] cardNo;

        //设备注册号 字节
        private byte[] deviceRegIdBytes;

        //设备注册号
        private String deviceRegId;
        //充值管理机序列号 字节
        private byte[] deviceSerialNoBytes;
        //充值管理机序列号
        private String deviceSerialNo;
        public Uplink(byte[] address, String name, String phone,
                      String idCard, int areaCode, byte[] cardNo) {
            super((byte) 0x01, address, FrameConstants.AFN_OPEN_CARD, new byte[UPLINK_DATA_LEN]);
            this.name = name;
            this.phone = phone;
            this.idCard = idCard;//身份证号
            this.areaCode = areaCode;
            this.cardNo = (cardNo == null) ? new byte[0] : cardNo;//卡号
            //9885 25 08 27 18 24 20
            // 检查地址域长度是否正确（开卡报文需要19字节地址域）

            if (address == null || address.length != 19) {
                throw new IllegalArgumentException("开卡报文地址域长度必须为19字节，实际长度: " +
                    (address == null ? 0 : address.length));
            }

            //从19字节地址域中提取设备注册号(前16字节)和设备序列号(后3字节)
            this.deviceRegIdBytes = Arrays.copyOfRange(address, 0, 16);
            this.deviceSerialNoBytes = Arrays.copyOfRange(address, 16, 19);

            //订单号为 deviceSerialNoBytes 后2个字节 +年月日时分秒
            try {
                this.orderId = OrderUtils.genOrderId(deviceSerialNoBytes);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            encode();
        }

        public Uplink(byte[] address, byte[] userData) throws Exception {
            super((byte) 0x01, address, FrameConstants.AFN_OPEN_CARD, userData);
            if (userData.length != UPLINK_DATA_LEN) {
                throwException("上行数据域长度错误，期望 {}, 实际 {}", UPLINK_DATA_LEN, userData.length);
            }
            decode( address,userData);
        }

        private void encode() {
            byte[] data = getUserData();

            // 订单号 → BCD (8字节)，左补 '0' 到 16 位
            byte[] orderIdBcd = BCD.strToBcd(StrUtil.padPre(orderId, 16, '0'));
            System.arraycopy(orderIdBcd, 0, data, OFFSET_ORDER_ID_UP, 8);

            // 姓名 → UTF-8 + 补0 (12字节)
            byte[] nameBytes = new byte[12];
            try {
                nameBytes = ChineseUtils.chineseToBytes(name);
            } catch (Exception ex) {
                log.warn("姓名编码失败，使用空字节填充: {}", name);
                Arrays.fill(nameBytes, (byte) 0);
            }
            System.arraycopy(nameBytes, 0, data, OFFSET_NAME_UP, 12);

            // 电话 → BCD (6字节)，补12位
            byte[] phoneBcd = BCD.strToBcd(StrUtil.padPre(phone, 12, '0'));
            System.arraycopy(phoneBcd, 0, data, OFFSET_PHONE_UP, 6);

            // 身份证 → ASCII (18字节)
            byte[] idBytes = idCard.toUpperCase().getBytes(ASCII);
            byte[] paddedId = padToLength(idBytes, 18, (byte) 0);
            System.arraycopy(paddedId, 0, data, OFFSET_ID_CARD_UP, 18);

            // 区域号 → BCD (2字节)
            byte[] areaCodeBcd = BCD.strToBcd(StrUtil.padPre(String.valueOf(areaCode), 4, '0'));
            System.arraycopy(areaCodeBcd, 0, data, OFFSET_AREA_CODE_UP, 2);

            // 卡号 → HEX，高位在前 (5字节)
            byte[] paddedCard = padToLength(cardNo, 5, (byte) 0);
            System.arraycopy(paddedCard, 0, data, OFFSET_CARD_NO_UP, 5);

            setUserData(data);
        }

        /**
         * 解码
         * @param address 地址
         * @param data 数据域
         */
        private void decode(byte[] address,byte[] data) {
            this.deviceRegIdBytes=Arrays.copyOfRange(address,1-1,1-1+16);
            this.deviceSerialNoBytes=Arrays.copyOfRange(address,17-1,17-1+3);

            // Hutool 默认是大端序，但我们是小端序，所以先反转
//            byte[] bigEndian = ByteUtil.reverse(extracted); // 转为大端序

            // 使用 ByteUtil.toLong / toBigInteger 还原
            // 注意：Hutool 没有直接支持 16 字节，需要用 BigInteger
//            java.math.BigInteger recovered = new java.math.BigInteger(1, bigEndian);

//            System.out.println("还原的设备注册 ID: " + recovered.toString(16).toUpperCase());




            this.deviceRegId=StrUtil.str(trimEnd(deviceRegIdBytes, (byte) 0), ASCII);
            this.deviceSerialNo=StrUtil.str(trimEnd(deviceSerialNoBytes, (byte) 0), ASCII);
            this.orderId = BCD.bcdToStr(ArrayUtil.sub(data, OFFSET_ORDER_ID_UP, 8));
            // 姓名解码
            byte[] nameBytes = ArrayUtil.sub(data, OFFSET_NAME_UP, 12);
            nameBytes=Arrays.copyOfRange(data,9-1,9-1+12);
            try {
                this.name = ChineseUtils.byteToChinese(nameBytes);
            } catch (Exception e) {
                log.warn("姓名解码失败，使用空字符串: {}", HexUtil.encodeHexStr(nameBytes));
                this.name = "";
            }
            byte[] _phone=Arrays.copyOfRange(data,21-1,21-1+6);
            String phoneTemp = BCD.bcdToStr(_phone);
            // 2. 去除前面所有的 0
            this.phone = phoneTemp.replaceFirst("^0+", "");
            //身份证号
            byte[] _idCard=Arrays.copyOfRange(data,27-1,27-1+18);
            this.idCard = StrUtil.str(trimEnd(_idCard, (byte) 0), ASCII).toUpperCase();

            // 修复区域码解析问题
            byte[] _areaCode=Arrays.copyOfRange(data,45-1,45-1+2);
            byte[] areaCodeBytes =_areaCode;
            String areaCodeStr = BCD.bcdToStr(areaCodeBytes);
            // 增加校验，确保areaCodeStr是有效的数字字符串
            if (areaCodeStr != null && areaCodeStr.matches("\\d+")) {
                this.areaCode = Integer.parseInt(areaCodeStr);
            } else {
                log.warn("区域码解析失败，使用默认值0。原始数据: {}", HexUtil.encodeHexStr(areaCodeBytes));
                this.areaCode = 0;
            }
            //卡号
            this.cardNo =Arrays.copyOfRange(data,47-1,47-1+5);
            ArrayUtil.reverse(this.deviceRegIdBytes); //
            this.deviceRegId=HexUtil.encodeHexStr( ArrayUtil.reverse(this.deviceRegIdBytes));
            this.deviceSerialNo=HexUtil.encodeHexStr(ArrayUtil.reverse(this.deviceSerialNoBytes));
        }

        // Getters
        public String getOrderId() { return orderId; }
        public String getName() { return name; }
        public String getPhone() { return phone; }
        public String getIdCard() { return idCard; }
        public int getAreaCode() { return areaCode; }
        public byte[] getCardNo() { return cardNo.clone(); }

        @Override
        public String toString() {
            return "OpenCardMessage.Uplink{" +
                    "deviceId="+deviceRegId+ ","+'\'' +
                    "deviceSerialNo="+deviceSerialNo+","+ '\'' +
                    "orderId='" + orderId + ","+'\'' +
                    "orderIdHex='" +  BCD.strToBcd(StrUtil.padPre(orderId, 16, '0')) +","+ '\'' +
                    ", name='" + name + ","+'\'' +
                    ", phone='" + phone + ","+'\'' +
                    ", idCard='" + idCard +","+ '\'' +
                    ", areaCode=" + areaCode +","+ '\'' +
                    ", cardNo=" + HexUtil.encodeHexStr(cardNo) + '\''+
                    '}';
        }
    }

    // ========================================================================
    // ========================= 下行报文：中心 → 设备 =========================
    // ========================================================================

    public static class Downlink extends OpenCardMessage {
        //订单编号
        private String orderId;
        private byte[] orderIdBytes;
        //农户注册号
        private byte[] farmerId;
        private byte status;

        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }

        public byte[] getOrderIdBytes() {
            return orderIdBytes;
        }

        public void setOrderIdBytes(byte[] orderIdBytes) {
            this.orderIdBytes = orderIdBytes;
        }

        public void setFarmerId(byte[] farmerId) {
            this.farmerId = farmerId;
        }

        public void setStatus(byte status) {
            this.status = status;
        }

        public Downlink(byte[] address, String orderId, byte[] farmerId, byte status) {
            super((byte) 0x00, address, FrameConstants.AFN_OPEN_CARD, new byte[DOWNLINK_DATA_LEN]);

            this.orderId = orderId;
            this.farmerId = (farmerId == null) ? new byte[16] : farmerId;
            this.status = status;

            encode();
        }

        public Downlink(byte[] address, byte[] userData) throws Exception {
            super((byte) 0x00, address, FrameConstants.AFN_OPEN_CARD, userData);
            System.out.println("下发userData数据长度="+userData.length);
            if (userData.length != DOWNLINK_DATA_LEN) {
                throwException("下行数据域长度错误，期望 {}, 实际 {}", DOWNLINK_DATA_LEN, userData.length);
            }
            decode(userData);
        }

        private void encode() {
            byte[] data = getUserData();

            // 订单号 → BCD (8字节)
//            byte[] orderIdBcd = BCD.strToBcd(StrUtil.padPre(orderId, 16, '0'));
//            System.arraycopy(orderIdBcd, 0, data, OFFSET_ORDER_ID_DOWN, 8);
            try {

                if (this.orderId.length() != 16) {
                    throw new IllegalArgumentException("订单号必须是 16 位数字字符串");
                }
                byte[] orderIdBcd =OrderUtils.genBcdOrderId(this.orderId,8);
                System.arraycopy(orderIdBcd, 0, data, OFFSET_ORDER_ID_DOWN, 8); // 将其复制到 data 中
            } catch (Exception e) {
                e.getMessage();
                throw new RuntimeException(e);
            }


            // 农户注册编号 → 16字节 HEX，低位在前
            byte[] paddedFarmer = padToLength(farmerId, 16, (byte) 0);
            ArrayUtil.reverse(paddedFarmer); // 低位在前，需要反转字节顺序
            System.arraycopy(paddedFarmer, 0, data, OFFSET_FARMER_ID_DOWN, 16);

            // 状态
            data[OFFSET_STATUS_DOWN] = status;

            setUserData(data);
        }

        private void decode(byte[] data) {
            this.orderIdBytes=Arrays.copyOfRange(data,1-1,1-1+8);
            this.orderId = BCD.bcdToStr(orderIdBytes);
            this.farmerId = Arrays.copyOfRange(data,9-1,9-1+16);
            ArrayUtil.reverse(this.farmerId); // 农户注册编号低位在前，需要反转字节顺序
            this.status = data[OFFSET_STATUS_DOWN];
        }

        public String getOrderId() { return orderId; }
        public byte[] getFarmerId() { return farmerId.clone(); }
        public byte getStatus() { return status; }
        public boolean isSuccess() { return status == 0x00; }
        public String getStatusDesc() {
            switch (status) {
                case 0x00: return "成功";
                case 0x01: return "身份已注册";
                case 0x02: return "其他原因失败";
                default: return "未知状态";
            }
        }

        @Override
        public String toString() {
            return "OpenCardMessage.Downlink{" +
                    "orderId='" + orderId +","+ '\'' +
                    "orderId BCD HEX='" +  BCD.strToBcd(StrUtil.padPre(orderId, 16, '0')) +","+ '\'' +
                    ", farmerId=" + HexUtil.encodeHexStr(farmerId) + ","+'\'' +
                    ", status=0x" + Integer.toHexString(status & 0xFF) +","+ '\'' +
                    ", statusDesc='" + getStatusDesc() + ","+'\'' +
                    '}';
        }
    }

    // ========================================================================
    // ============================= 工厂方法 =================================
    // ========================================================================

    /**
     * 解析开卡报文
     * @param address 设备地址
     * @param control 控制域
     * @param userData 数据域
     * @throws Exception 解析异常
     */
    public static OpenCardMessage parse(byte[] address, byte control, byte[] userData) throws Exception {
        if (control == 0x01) {
            return new Uplink(address, userData);
        } else if (control == 0x00) {
            return new Downlink(address, userData);
        } else {
            String hex = HexUtil.encodeHexStr(new byte[]{control});
            throwException("不支持的控制域: 0x{}", hex);
        }
        return null; // unreachable
    }

    // ========================================================================
    // ============================= 工具方法 =================================
    // ========================================================================

    /**
     * 记录警告日志并抛出 Exception
     */
    private static void throwException(String format, Object... args) throws Exception {
        String msg = StrUtil.format(format, args);
        log.warn("OpenCardMessage 解析异常: {}", msg);
        throw new Exception(msg);
    }

    /**
     * 测试方法，用于演示上行和下行数据的组装和解析
     */
    public static void main(String[] args) {
        try {
            System.out.println("=== OpenCardMessage 测试 ===");

            // 准备测试数据
            byte[] testAddress = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                                           0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
                                           0x11, 0x12, 0x13, 0x14};

            String orderId = "1234567890123456";
            String name = "张三";
            String phone = "13812345678";
            String idCard = "110101199001011234";
            int areaCode = 1234;
            byte[] cardNo = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05};

            // 测试上行报文组装
            System.out.println("\n--- 测试上行报文组装 ---");
            Uplink uplink = new Uplink(testAddress,  name, phone, idCard, areaCode, cardNo);
            System.out.println("上行报文组装完成: " + uplink.toString());

            // 获取上行报文的字节数组表示（模拟发送）
            byte[] uplinkAddress = uplink.getAddress();
            byte uplinkControl = uplink.getControl();
            byte uplinkAfn = uplink.getAfn();
            byte[] uplinkUserData = uplink.getUserData();

            System.out.println("上行报文地址域: " + HexUtil.encodeHexStr(uplinkAddress));
            System.out.println("上行报文控制域: 0x" + Integer.toHexString(uplinkControl & 0xFF));
            System.out.println("上行报文AFN: 0x" + Integer.toHexString(uplinkAfn & 0xFF));
            System.out.println("上行报文用户数据: " + HexUtil.encodeHexStr(uplinkUserData));

            // 测试上行报文解析
            System.out.println("\n--- 测试上行报文解析 ---");
            Uplink parsedUplink = new Uplink(uplinkAddress, uplinkUserData);
            System.out.println("解析后的上行报文: " + parsedUplink.toString());
            System.out.println("订单号: " + parsedUplink.getOrderId());
            System.out.println("姓名: " + parsedUplink.getName());
            System.out.println("电话: " + parsedUplink.getPhone());
            System.out.println("身份证: " + parsedUplink.getIdCard());
            System.out.println("区域码: " + parsedUplink.getAreaCode());
            System.out.println("卡号: " + HexUtil.encodeHexStr(parsedUplink.getCardNo()));

            // 测试下行报文组装
            System.out.println("\n--- 测试下行报文组装 ---");
            byte[] farmerId = new byte[]{(byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66, (byte) 0x77, (byte) 0x88,
                                        (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66, (byte) 0x77, (byte) 0x88};
            byte status = 0x00; // 成功
            Downlink downlink = new Downlink(testAddress, orderId, farmerId, status);
            System.out.println("下行报文组装完成: " + downlink.toString());

            // 获取下行报文的字节数组表示（模拟发送）
            byte[] downlinkAddress = downlink.getAddress();
            byte downlinkControl = downlink.getControl();
            byte downlinkAfn = downlink.getAfn();
            byte[] downlinkUserData = downlink.getUserData();

            System.out.println("下行报文地址域: " + HexUtil.encodeHexStr(downlinkAddress));
            System.out.println("下行报文控制域: 0x" + Integer.toHexString(downlinkControl & 0xFF));
            System.out.println("下行报文AFN: 0x" + Integer.toHexString(downlinkAfn & 0xFF));
            System.out.println("下行报文用户数据: " + HexUtil.encodeHexStr(downlinkUserData));

            // 测试下行报文解析
            System.out.println("\n--- 测试下行报文解析 ---");
            Downlink parsedDownlink = new Downlink(downlinkAddress, downlinkUserData);
            System.out.println("解析后的下行报文: " + parsedDownlink.toString());
            System.out.println("订单号: " + parsedDownlink.getOrderId());
            System.out.println("农户ID: " + HexUtil.encodeHexStr(parsedDownlink.getFarmerId()));
            System.out.println("状态码: 0x" + Integer.toHexString(parsedDownlink.getStatus() & 0xFF));
            System.out.println("是否成功: " + parsedDownlink.isSuccess());
            System.out.println("状态描述: " + parsedDownlink.getStatusDesc());

            System.out.println("\n=== 测试完成 ===");
        } catch (Exception e) {
            System.err.println("测试过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
